#include <linux/workqueue.h>
#include<linux/module.h>
#include<linux/slab.h>

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Guanjun He heguanbo@gmail.com");


//for kernel before 2.6.20
/*
struct work_struct {
	unsigned long pending;
	struct list_head entry;
	void (*func)(void *);
	void *data;
	void *wq_data;
	struct timer_list timer;
};*/
void work_fn(void *a )
{
	printk("************* sequence num: %d ***************\n", *((int*)a));
	printk("************* work queue test module works! ***************\n");
}


//for kernel 2.6.20 or later
/*struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};*/
struct my_work{
	int seq;
	struct work_struct real_work;//works ok.  this must be a struct, not a pointer.
};
struct my_work2{
	int seq;
	//can not be a pointer, not worked.if it's a pointer,then no way to get the original address of the member in the struct my_work2.
	struct work_struct *real_work;
};
void work_fn1(struct work_struct *wq )
{
	if(wq != NULL)
	{
		struct my_work* tmp = container_of(wq, struct my_work, real_work);
		printk("************* pre-defined seq: %d ***************\n", tmp->seq);
	}
}
void work_fn2(struct work_struct *wq )
{
	if(wq != NULL)
	{
		//if in heap, no way to get the original address of the member in the struct my_work2
		struct my_work2* tmp = container_of(&wq/*even this can not work*/, struct my_work2, real_work);
		printk("************* seq: %d ***************\n", tmp->seq);
	}
}

//create a new seperate workqueue
struct workqueue_struct *create_wq(const char* name)
{
	return create_workqueue(name);
}

int addwork_to_wq(struct workqueue_struct *wq, struct work_struct *work)
{
	return queue_work(wq, work);
}



static struct workqueue_struct *test_wq = NULL;
static int a = 10;
static int i = 0;

static int wq_init(void)
{


	/**************************/
	/*for kernel before 2.6.20*/
	/**************************/
	//a.for new created workqueue:
	test_wq = create_wq("ktest_wq");
	if(test_wq == NULL)
	{
		return 0;
	}

	for (i=0;i<100;i++)
	{
		struct work_struct *mywork =  kmalloc(sizeof(struct work_struct), GFP_KERNEL);
		INIT_WORK(mywork, work_fn, &i);
		addwork_to_wq(test_wq, mywork);
	}

	//b.for pre-defined workqueue:
	/*printk("step0: \n");
	for (i=0;i<100;i++)
	{
		//printk("step %d: \n", i+1);
		struct work_struct *mywork =  kmalloc(sizeof(struct work_struct), GFP_KERNEL);
		INIT_WORK(mywork, work_fn, &i);
		schedule_work(mywork);
		//struct work_struct mywork;  //this can not work,must be in heap!
		//INIT_WORK(&mywork, work_fn, &i);
		//schedule_work(&mywork);
	}*/






	/**************************/
	//for kernel 2.6.20 or later
	/**************************/
	//1.ok
	/*for (i=0;i<100;i++)
	{
		struct my_work *work2 = kmalloc(sizeof(struct my_work), GFP_KERNEL);
		work2->seq = i;
		INIT_WORK(&(work2->real_work), work_fn1);
		addwork_to_wq(test_wq, &(work2->real_work));
	}*/
	//2.not ok.
	/*for (i=0;i<100;i++)
	{
		struct work_struct *work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
		struct my_work2 *work2 = kmalloc(sizeof(struct my_work), GFP_KERNEL);
		work2->real_work = work1;
		work2->seq = i;
		//INIT_WORK(work2->real_work, work_fn2);
		//this can not work in heap,from here we lost the original address of the memeber in the struct,so we can not get the address of the container anymore.
		INIT_WORK(work2->real_work, work_fn2);
		addwork_to_wq(test_wq, work2->real_work);
		printk("ggggggggggggg\n");
	}*/


	//3.for pre-defined workqueue:
	/*for (i=0;i<100;i++)
	{
		struct my_work *work2 = kmalloc(sizeof(struct my_work), GFP_KERNEL);
		work2->seq = i;
		INIT_WORK(&(work2->real_work), work_fn1);
		schedule_work(&(work2->real_work));
	}*/

	return 0;
}

static void wq_exit(void)
{
	//for new workqueue
	if(test_wq != NULL)
	{
		flush_workqueue(test_wq);
		destroy_workqueue(test_wq);
	}




	//for pre-defined workqueue
	//flush_scheduled_work();

	return;
}





module_init( wq_init );
module_exit( wq_exit );


